#!/usr/bin/python
import re
import os
import md5

class Component(object):
  """A dictionary like object, that can cache results on a storage device."""
  def __init__(self, cache_path):
      self.cache_path = cache_path
      if not os.path.isdir(cache_path):
         os.mkdir(cache_path)

  def key2path(self, key):
      return os.path.join(self.cache_path, key)

  def has_key(self, key):
      return os.path.isfile(self.key2path(key))

class QCache(Component):

  def __init__(self, path='.cache'):
      Component.__init__(self, path)

  def getitem(self, key):
      if not isinstance(key, str):
         raise TypeError()
      cacheFullPath = self.key2path(key)
      if os.path.isfile(cacheFullPath):
          return file(cacheFullPath, 'rb').read()
      else:
          raise IndexError()

  def setitem(self, key, value):
      if not isinstance(key, str):
         raise TypeError()
      cacheFullPath = self.key2path(key)
      if os.path.isfile(cacheFullPath):
         os.unlink(cacheFullPath)
      f = file(cacheFullPath,'wb')
      try:
          f.write(md5.new(value).hexdigest())
      finally:
          f.close()

  def delitem(self, key):
      if not isinstance(key, str):
         raise TypeError()
      cacheFullPath = self.key2path(key)
      if os.path.isfile(cacheFullPath):
          os.unlink(cacheFullPath)


# Cache filename construction (original borrowed from Venus http://intertwingly.net/code/venus/)
re_url_scheme    = re.compile(r'^\w+://')

class PCache(Component):
    """Uses a local directory as a store for cached files.
    Not really safe to use if multiple threads or processes are going to 
    be running on the same cache.
    """
    def __init__(self, path='.cache'):
        Component.__init__(self, path)

    def filename(self, key):
        """Return a filename suitable for the cache.
        """
        try:
            if re_url_scheme.match(key):
                if isinstance(key,str):
                    key = key.decode('utf-8')
                key = key.encode('idna')
        except UnicodeError:
            pass
        if isinstance(key, unicode):
           key = key.encode('utf-8')
        return md5.new(key).hexdigest()

    def getitem(self, key):
        retval = None
        cacheFullPath = self.key2path(self.filename(key))
        try:
            f = file(cacheFullPath, "r")
            retval = f.read()
            f.close()
        except IOError:
            pass
        return retval

    def setitem(self, key, value):
        cacheFullPath = self.key2path(self.filename(key))
        f = file(cacheFullPath, "w")
        f.write(value)
        f.close()

    def delitem(self, key):
        cacheFullPath = self.key2path(self.filename(key))
        if os.path.exists(cacheFullPath):
            os.remove(cacheFullPath)


